#include "napi/native_api.h"
#include "lion_random.h"
#include "sha256.h"
#include "sha3.h"
#include <cstring>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "pkcs1oaep.h"

static std::string uint8_to_hex_string(const uint8_t *v, const size_t s) {

    std::stringstream ss;

    ss << std::hex << std::setfill('0');

    for (int i = 0; i < s; i++) {
        ss << std::hex << std::setw(2) << static_cast<int>(v[i]);
    }

    return ss.str();
}

static napi_value oaep_sha3_256_mgf1_sha256_encoding(napi_env env, napi_callback_info info) {

    size_t argc = 2;

    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    // 获取字符串的长度
    size_t length = 0;
    napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &length);
    // 传入一个非字符串 napi_get_value_string_utf8接口会返回napi_string_expected
    if (status != napi_ok) {
        return nullptr;
    }

    char *buf = new char[length + 1];
    std::memset(buf, 0, length + 1);
    napi_get_value_string_utf8(env, args[0], buf, length + 1, &length);

    size_t key_len = 256;

    char *em = (char *)calloc(1, key_len);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
    int8_t ret = pkcs1oaepEncode(key_len, buf, strlen(buf), em, sha3, 32, mgf1, sha256_b, 32, random_bytes, "", 0);
#pragma clang diagnostic pop
    if (ret != 0) {
        return nullptr;
    }

    napi_value result = nullptr;

    // 创建一个新的ArrayBuffer
    char *buffer = nullptr;
    napi_status status1 = napi_create_arraybuffer(env, 256, reinterpret_cast<void **>(&buffer), &result);
    for (int i = 0; i < 256; i++) {
        buffer[i] = em[i];
    }
    if (status1 != napi_ok) {
        return nullptr;
    };

    free(em);
    free(buf);

    return result;
}

static napi_value sha3_256(napi_env env, napi_callback_info info) {

    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    void *data = nullptr;
    size_t data_length = 0;
    napi_status status = napi_get_arraybuffer_info(env, args[0], &data, &data_length);

    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to get ArrayBuffer info");
        return nullptr;
    }

    size_t sha_len = 32;
    uint8_t sha[sha_len];
    memset(sha, 0, sizeof(sha));

    sha3(data, data_length, sha, sha_len);

    std::string result = uint8_to_hex_string(sha, sha_len);

    napi_value res = nullptr;

    napi_status status1 = napi_create_string_utf8(env, result.c_str(), NAPI_AUTO_LENGTH, &res);

    if (status1 != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to create UTF-8 string");
        return nullptr;
    }

    return res;
}


EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    
    napi_property_descriptor native_methods[] = {
        {"sha3_256", nullptr, sha3_256, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"oaepSha3256Mgf1Sha256Encoding", nullptr, oaep_sha3_256_mgf1_sha256_encoding, nullptr, nullptr, nullptr,
         napi_default, nullptr}};

    napi_define_properties(env, exports, sizeof(native_methods) / sizeof(native_methods[0]), native_methods);

    return exports;
}
EXTERN_C_END

static napi_module hlCryptoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "hl_crypto",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterHl_cryptoModule(void) { napi_module_register(&hlCryptoModule); }
